﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using DZLib.ExtendFramework.Generic;

namespace DZLib.Math.Basic
{
    public class Vector : IDictionary<String, double>
    {
        private Dictionary<String, double> _content;
        private bool _isModified = true;
        private double _mod = 0;
        private int _dimension = 0;

        private void Update()
        {
            double power = 0;
            foreach (var item in this)
            {
                power += System.Math.Pow(item.Value, 2);
            }
            this._mod = System.Math.Sqrt(power);
            this._dimension = this._content.Count;
            this._isModified = false;
        }

        public int Dimension
        {
            get
            {
                if (_isModified)
                {
                    this.Update();
                }
                return this._dimension;
            }
        }

        public double Mod
        {
            get
            {
                if (_isModified)
                {
                    this.Update();
                }
                return this._mod;
            }
        }

        public bool KeyEqual(Vector target)
        {
            var srcKeyList = this.Keys.OrderBy(i => i);
            var targetKeyList = target.Keys.OrderBy(i => i);
            return srcKeyList.SequenceEqual(targetKeyList);
        }

        public double InnerProduction(Vector target)
        {
            var srcKeyList = this.Keys.OrderBy(i => i);
            var targetKeyList = target.Keys.OrderBy(i => i);
            if (srcKeyList.SequenceEqual(targetKeyList))
            {
                double result = 0;
                for (int i = 0; i < this.Dimension; i++)
                {
                    var index = srcKeyList.ElementAt(i);
                    result = this[index] * target[index];
                }
                return result;
            }
            else
            {
                throw new ArgumentException("The keys of each vector should be sequential equal");
            }
        }

        public static double operator *(Vector v1, Vector v2)
        {
            return v1.InnerProduction(v2);
        }

        public double CosineIncludeAngle(Vector target)
        {
            if (this.Dimension == target.Dimension)
            {
                var mod1 = this.Mod == 0 ? 1 : this.Mod;
                var mod2 = target.Mod == 0 ? 1 : target.Mod;
                var innerProduct = this * target;
                return innerProduct / (mod1 * mod2);
            }
            else
            {
                throw new ArgumentException("The dimension of two vector should be exactly the same");
            }
        }

        public Vector UpdateCentroid(Vector centroid)
        {
            var cenKeyList = centroid.Keys.OrderBy(i => i);
            var vecKeyList = this.Keys.OrderBy(i => i);
            var newKeyList = cenKeyList.Union(vecKeyList);
            foreach (var item in newKeyList)
            {
                if (cenKeyList.Contains(item))
                {
                    if (vecKeyList.Contains(item))
                    {
                        centroid[item] += this[item];
                    }
                }
            }
            return centroid;
        }

        public void AlignWithCentroid(Vector centroid)
        {
            var srcKeyList = this.Keys.OrderBy(i => i);
            var centroidKeyList = centroid.Keys.OrderBy(i => i);
            if (!this.KeyEqual(centroid))
            {
                foreach (var item in centroidKeyList)
                {
                    if (!srcKeyList.Contains(item))
                    {
                        this.Add(item, 0);
                    }
                }
            }
        }

        public Vector()
        {
            this._content = new Dictionary<string, double>();
        }

        public Vector(Dictionary<String, double> content)
        {
            this._isModified = true;
            this._content = content;
        }

        public Vector(IEnumerable<KeyValuePair<String, double>> list)
        {
            this._content = new Dictionary<string, double>();
            foreach (var item in list)
            {
                this.Add(item);
            }
            this._isModified = true;
        }

        public Vector(IEnumerable<String> keyList)
        {
            this._content = new Dictionary<string, double>();
            foreach (var item in keyList)
            {
                this._content.Add(item, 0);
            }
            this._isModified = true;
        }

        public void Add(string key, double value)
        {
            this._isModified = true;
            this._content.Add(key: key, value: value);
        }

        public bool ContainsKey(string key)
        {
            return this._content.ContainsKey(key: key);
        }

        public ICollection<string> Keys
        {
            get
            {
                return this._content.Keys;
            }
        }

        public bool Remove(string key)
        {
            this._isModified = true;
            return this.Remove(key: key);
        }

        public bool TryGetValue(string key, out double value)
        {
            return this._content.TryGetValue(key: key, value: out value);
        }

        public ICollection<double> Values
        {
            get
            {
                return this._content.Values;
            }
        }

        public double this[string key]
        {
            get
            {
                return this._content[key];
            }
            set
            {
                if (this._content[key] != value)
                {
                    this._isModified = true;
                    this._content[key] = value;
                }
            }
        }

        public void Add(KeyValuePair<string, double> item)
        {
            this._isModified = true;
            this._content.Add(item.Key, item.Value);
        }

        public void Clear()
        {
            this._content.Clear();
        }

        public bool Contains(KeyValuePair<string, double> item)
        {
            return this._content.Contains(item);
        }

        public void CopyTo(KeyValuePair<string, double>[] array, int arrayIndex)
        {
            if (arrayIndex >= 0 && arrayIndex < this.Dimension)
            {
                var count = this.Dimension - arrayIndex;
                var tmp = this._content.ToArray();
                array = this._content.Sub(arrayIndex).ToArray();
                return;
            }
            throw new IndexOutOfRangeException();
        }

        public int Count
        {
            get
            {
                return this.Dimension;
            }
        }

        public bool IsReadOnly
        {
            get
            {
                return false;
            }
        }

        public bool Remove(KeyValuePair<string, double> item)
        {
            this._isModified = true;
            return this._content.Remove(item.Key);
        }

        public IEnumerator<KeyValuePair<string, double>> GetEnumerator()
        {
            return new VectorEnumerator(this._content);
        }

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return this.GetEnumerator();
        }
    }

    public class VectorEnumerator : IEnumerator<KeyValuePair<string, double>>
    {
        private Dictionary<String, double> _content;
        private int _position = -1;

        public VectorEnumerator(Dictionary<String, double> content)
        {
            this._content = content;

        }

        public KeyValuePair<string, double> Current
        {
            get
            {
                if (this._content.IndexIsInRange(_position))
                {
                    return this._content.ElementAt(_position);
                }
                throw new IndexOutOfRangeException();
            }
        }

        public void Dispose()
        {

        }

        object System.Collections.IEnumerator.Current
        {
            get
            {
                return this.Current;
            }
        }

        public bool MoveNext()
        {
            _position++;
            return (_position < this._content.Count);
        }

        public void Reset()
        {
            _position = -1;
        }
    }
}
